﻿//******************************************************************************************************
//  LatencyAnalyst.cs - Gbtc
//
//  Copyright © 2010, Grid Protection Alliance.  All Rights Reserved.
//
//  Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
//  the NOTICE file distributed with this work for additional information regarding copyright ownership.
//  The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may
//  not use this file except in compliance with the License. You may obtain a copy of the License at:
//
//      http://www.opensource.org/licenses/eclipse-1.0.php
//
//  Unless agreed to in writing, the subject software distributed under the License is distributed on an
//  "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
//  License for the specific language governing permissions and limitations.
//
//  Code Modification History:
//  ----------------------------------------------------------------------------------------------------
//  08/06/2010 - Stephen C. Wills
//       Generated original version of source code.
//
//******************************************************************************************************

using System.IO;
using System.Text;
using TestBenchApi;
using TVA.PhasorProtocols;

namespace TestBenchAnalyst
{
    /// <summary>
    /// Represents a test bench analyst that determines minimum, maximum, and average latency of measurements.
    /// </summary>
    public class LatencyAnalyst : Analyst
    {

        #region [ Members ]

        // Fields

        private string m_latencyFilePath;

        private long m_minimumLatency;
        private long m_maximumLatency;
        private long m_total;
        private int m_count;

        #endregion

        #region [ Constructors ]

        /// <summary>
        /// Creates a new instance of the <see cref="LatencyAnalyst"/> class.
        /// </summary>
        public LatencyAnalyst()
        {
            m_minimumLatency = -1;
            m_maximumLatency = -1;
        }

        #endregion

        #region [ Properties ]

        /// <summary>
        /// Gets the minimum latency calculated by the <see cref="LatencyAnalyst"/>.
        /// </summary>
        public long MinimumLatency
        {
            get
            {
                return m_minimumLatency;
            }
        }

        /// <summary>
        /// Gets the maximum latency calculated by the <see cref="LatencyAnalyst"/>.
        /// </summary>
        public long MaximumLatency
        {
            get
            {
                return m_maximumLatency;
            }
        }

        /// <summary>
        /// Gets the average latency calculated by the <see cref="LatencyAnalyst"/>.
        /// </summary>
        public long AverageLatency
        {
            get
            {
                if (m_count > 0)
                    return m_total / m_count;
                else
                    return -1;
            }
        }

        #endregion

        #region [ Methods ]

        /// <summary>
        /// Initializes the <see cref="LatencyAnalyst"/>.
        /// </summary>
        /// <param name="packageFilePath">The path to the package file.</param>
        public override void Initialize(string packageFilePath)
        {
            base.Initialize(packageFilePath);

            string outputFile = PackageFile.Receiver.OutputFile;
            m_latencyFilePath = outputFile.Substring(0, outputFile.LastIndexOf('.')) + " - latency.txt";
        }

        /// <summary>
        /// Determines the latency of the frame and makes the appropriate calculations.
        /// </summary>
        /// <param name="sourceFrame">The original frame sent by the generators.</param>
        /// <param name="receivedFrame">The corresponding frame as received by the receiver.</param>
        /// <param name="timeReceived">The time at which the frame was received by the receiver.</param>
        public override void CompareFrames(IDataFrame sourceFrame, IDataFrame receivedFrame, long timeReceived)
        {
            // If receivedFrame is null, then the concentrator missed a frame.
            // We cannot calculate the latency of missed frames so we don't even try.
            if (receivedFrame != null)
            {
                long latency = timeReceived - sourceFrame.Timestamp;

                if (latency < m_minimumLatency || m_minimumLatency < 0)
                    m_minimumLatency = latency;

                if (latency > m_maximumLatency || m_maximumLatency < 0)
                    m_maximumLatency = latency;

                m_total += latency;
                m_count++;
            }
        }

        /// <summary>
        /// Stops the <see cref="LatencyAnalyst"/>.
        /// </summary>
        protected override void OnAnalystStopped()
        {
            base.OnAnalystStopped();

            TextWriter writer = null;
            StringBuilder output = new StringBuilder();

            output.Append("Minimum Latency: ");
            output.Append(m_minimumLatency);
            output.AppendLine();

            output.Append("Maximum Latency: ");
            output.Append(m_maximumLatency);
            output.AppendLine();

            output.Append("Average Latency: ");
            output.Append(AverageLatency);

            try
            {
                writer = new StreamWriter(new FileStream(m_latencyFilePath, FileMode.Create, FileAccess.Write));
                writer.Write(output.ToString());
            }
            finally
            {
                if (writer != null)
                    writer.Close();
            }
        }

        #endregion

    }
}
